﻿using System;

namespace theeg.GeometryWpf
{
    /// <summary>
    /// Different types of shapes for ranged queries in a kdTree
    /// </summary>
    public enum ShapeType
    {
        Unknown = 0,
        Rectangle = 1,
        Circle = 2,
        Polygon = 3,
        Freeform = 4,
        Multishape = 5,
        Pointcloud = 6,
        Polyline = 7,
    }

    /// <summary>
    /// return values for determining whether a shape contains the region of a node in the kdTree
    /// </summary>
    public enum ShapeOverlapping
    {
        Contains = 1,
        Intersects = 2,
        Outside = 3
    }

    /// <summary>
    /// Shape base class for ranged queries in a hilbertTree
    /// extended by Rectangle, Circle, Polygon, Freeform etc.
    /// </summary>
    public abstract class ComputationalShape
    {
        /// <summary>
        /// private field for bounding box. Each descendant of ComputationalShape MUST
        /// set the BoundingBox property to fill this field.
        /// </summary>
        private ComputationalRectangle _boundingBox;
        /// <summary>
        /// the bounding rectangle for fast rejecting
        /// </summary>
        public ComputationalRectangle BoundingBox
        {
            get
            {
                return _boundingBox;
            }
            set
            {
                _boundingBox = value;
            }
        }

        /// <summary>
        /// Gets the width of the shape. By default it is defined by the bounding box extents. 
        /// </summary>
        public virtual double Width
        {
            get
            {
                return _boundingBox.GetWidth();
            }
        }

        /// <summary>
        /// Gets the width of the shape. By default it is defined by the bounding box extents. 
        /// </summary>
        public virtual double Height
        {
            get
            {
                return _boundingBox.GetHeight();
            }
        }

        /// <summary>
        /// Test if Shape contains a certain point
        /// </summary>
        /// <param name="pt">point to test</param>
        /// <returns></returns>
        public virtual bool Contains(System.Windows.Point pt)
        {
            return _boundingBox.Contains(pt);
        }

        /// <summary>
        /// Test if Shape contains a certain point
        /// </summary>
        /// <param name="x">x coordinate to test</param>
        /// <param name="y">y coordinate to test</param>
        /// <returns>true if point is inside or at the border of the shape.</returns>
        public virtual bool Contains(double x, double y)
        {
            return _boundingBox.Contains(x, y);
        }

        /// <summary>
        /// Test if Shape contains another Rectangle
        /// </summary>
        /// <param name="rect">rectangle to test</param>
        /// <returns>ShapeOverlapping code</returns>
        public virtual ShapeOverlapping Contains(ComputationalRectangle rect)
        {
            return _boundingBox.Contains(rect);
        }

        private static double GetDistance(ComputationalRectangle rect, System.Windows.Point pt)
        {
            if (pt.X < rect.L)
            {
                if (pt.Y < rect.T)
                {
                    //distance to top left
                    return Math.Sqrt((rect.L - pt.X) * (rect.L - pt.X) + (pt.Y - rect.T) * (pt.Y - rect.T));
                }
                if (pt.Y > rect.B)
                {
                    //distance to bottom left
                    return Math.Sqrt((rect.L - pt.X) * (rect.L - pt.X) + (pt.Y - rect.B) * (pt.Y - rect.B));
                }
                //distance to left
                return rect.L - pt.X;
            }
            if (pt.X > rect.R)
            {
                if (pt.Y < rect.T)
                {
                    //distance to top right
                    return Math.Sqrt((rect.R - pt.X) * (rect.R - pt.X) + (pt.Y - rect.T) * (pt.Y - rect.T));
                }
                if (pt.Y > rect.B)
                {
                    //distance to bottom right
                    return Math.Sqrt((rect.R - pt.X) * (rect.R - pt.X) + (pt.Y - rect.B) * (pt.Y - rect.B));
                }
                //distance to right
                return pt.X - rect.R;
            }
            if (pt.Y < rect.T)
            {
                //distance to top
                return rect.T - pt.Y;
            }
            if (pt.Y > rect.B)
            {
                //distance to bottom
                return pt.Y - rect.B;
            }
            //inside
            return 0;
        }

        /// <summary>
        /// Get distance between a point and a shape
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public virtual double GetDistanceFrom(System.Windows.Point pt)
        {
            return GetDistance(_boundingBox, pt);
        }
    }
}
